package kr.ac.kaist.idb.csnql.clause;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Vector;

import kr.ac.kaist.idb.csnql.lib.Base64Coder;

/**
 * 
 * @brief Concrete Query class
 * @author Jongheon Park (korex527@kaist.ac.kr) edited by minjee
 *
 */
public class SNQLQuery implements Serializable, SNQLStatement, SNQLExp 
{
	
	/**
	 * @brief Serial version UID which is automatically generated
	 */
	private static final long serialVersionUID = -3595589518674206890L;

	public long queryID;
	
	public ArrayList<Integer> targetRelatedCellInfo;
	
	ArrayList<String> queryPath;
	
	String targetRegionCoordinate;
	
	String query;
	
	String queryExecutionStartTime;
	
	/**
	 * @brief SELECT Item list
	 */
    public Vector<Object> select_;
    /**
     * @brief FROM Item list
     */
    public SNQLFromItem from_;
    /**
     * @brief WHERE clause
     */
    public SNQLExp where_;
    /**
     * @brief SAMPLE PERIOD-FOR clause
     */
    public SNQLSample sample_;
    /**
     * @brief ON EVENT clause
     */
    public SNQLEvent event_;
    /**
     * @brief CASE-WHEN clause
     */
    public SNQLCase case_;
    /**
     * @brief WITHIN clause
     */
    public SNQLWithin within_;   
    /**
     * @brief true, if the query containes ON EVENT clause 
     */
    public boolean isevent_;
    /**
     * @brief true, if the query containes EVENT Definition clause
     */
    public boolean iseventDefined;
    /**
     * @brief Pre-defined event name
     */
    public String eventDefinedName;
    /**
     * @brief Pre-defined event predicate
     */
    public SNQLExpression eventDefinedExp;

    /**
     * @brief Instantiates a new SNQLQuery
     */
    public SNQLQuery()
    {
    	queryID = 0;
    	select_ = new Vector<Object>();
    	from_ = new SNQLFromItem();
    	queryPath = new ArrayList<String>();
    	targetRelatedCellInfo = new ArrayList<Integer>();
    	targetRegionCoordinate = new String();
    	query = new String();
    	queryExecutionStartTime = new String();
    	event_ = new SNQLEvent();
        where_ = new SNQLExpression();
        isevent_ = false;
        sample_ = new SNQLSample();
        case_ = new SNQLCase();
        within_ = new SNQLWithin();
        iseventDefined = false;
        eventDefinedName = new String();
        eventDefinedExp = new SNQLExpression();
    }
    
    
    public String getQueryExecutionStartTime() {
		return queryExecutionStartTime;
	}
	public void setQueryExecutionStartTime(String queryExecutionStartTime) {
		this.queryExecutionStartTime = queryExecutionStartTime;
	}
	public String getQuery() {
		return query;
	}
	public void setQuery(String query) {
		this.query = query;
	}
	public String getTargetRegionCoordinate() {
		return targetRegionCoordinate;
	}
	public void setTargetRegionCoordinate(String targetRegionCoordinate) {
		this.targetRegionCoordinate = targetRegionCoordinate;
	}
	public long getQueryID() {
		return queryID;
	}
	public void setQueryID(long queryID) {
		this.queryID = queryID;
	}
	public ArrayList<String> getQueryPath() {
		return queryPath;
	}
	public void setQueryPath(ArrayList<String> queryPath) {
		this.queryPath = queryPath;
	}
	public ArrayList<Integer> getTargetRelatedCellInfo() {
		return targetRelatedCellInfo;
	}
	public void setTargetRelatedCellInfo(ArrayList<Integer> targetRelatedCellInfo) {
		this.targetRelatedCellInfo = targetRelatedCellInfo;
	}
	/**
     * @param ievent New event
     * @brief Set the event
     */
    public void setEvent_(SNQLEvent ievent)
    {
    	isevent_ = true;
    	event_ = ievent;
    }

    
    
    /**
     * @param vector New select
     * @brief Set the select
     */
    public void setSelect_(Vector<Object> vector)
    {
        select_ = vector;
    }

    /**
     * @param vector New from
     * @brief Set the from
     */
    public void setFrom_(SNQLFromItem ifrom)
    {
        from_ = ifrom;
    }

    /**
     * @param zexp New where
     * @brief Set the where
     */
    public void setWhere_(SNQLExp zexp)
    {
        where_ = zexp;
    }
    
    /**
     * @param isample New sample
     * @brief Set the sample
     */
    public void setSample_(SNQLSample isample)
    {
    	sample_ = isample;
    }
    
    /**
     * @param icase New case
     * @brief Set the case
     */
    public void setCase_(SNQLCase icase)
    {
    	case_ = icase;
    }
    
    /**
     * @param iwithin New within
     * @brief Set the within
     */
    public void setWithin_(SNQLWithin iwithin)
    {
    	within_ = iwithin;
    }

    /**
     * @return the select
     * @brief Get the select
     */
    public Vector<Object> getSelect()
    {
        return select_;
    }

    /**
     * @return the from
     * @brief Get the from
     */
    public SNQLFromItem getFrom()
    {
        return from_;
    }

    /**
     * @return the where
     * @brief Get the where
     */
    public SNQLExp getWhere()
    {
        return where_;
    }
    
    /**
     * @return the sample
     * @brief Get the sample
     */
    public SNQLSample getSample()
    {
        return sample_;
    }
    
    /**
     * @return the case
     * @brief Get the case
     */
    public SNQLCase getCase()
    {
    	return case_;
    }
    
    /**
     * @return the within
     * @brief Get the within
     */
    public SNQLWithin getWithin() {
		return within_;
	}

	public SNQLEvent getEvent_() {
		return event_;
	}

	/**
	 * @return true, if it is event definition query
	 * @brief Check whether it is event definition query
	 */
	public boolean getIseventDefined() {
		return iseventDefined;
	}

	/**
	 * @param iseventDefined the event definition query flag
	 * @brief Set the event definition query flag
	 */
	public void setIseventDefined(boolean iseventDefined) {
		this.iseventDefined = iseventDefined;
	}

	/**
	 * @return the pre-defined event name
	 * @brief Get the pre-defined event name
	 */
	public String getEventDefinedName() {
		return eventDefinedName;
	}

	/**
	 * @param eventDefinedName New event predicate
	 * @brief Set the pre-defined event predicate
	 */
	public void setEventDefinedName(String eventDefinedName) {
		this.eventDefinedName = eventDefinedName;
	}

	/**
	 * @return the pre-defined event predicate
	 * @brief Get the pre-defined event predicate
	 */
	public SNQLExpression getEventDefinedExp() {
		return eventDefinedExp;
	}

	/**
	 * Sets the event defined exp.
	 *
	 * @param eventDefinedExp New event defined exp
	 * @brief Set the event defined exp
	 */
	public void setEventDefinedExp(SNQLExp eventDefinedExp) {
		this.eventDefinedExp = (SNQLExpression) eventDefinedExp;
	}
	
	public String toString()
    {
		StringBuffer stringbuffer = new StringBuffer();
		
		if(isevent_)
        	stringbuffer.append("ON EVENT " + event_.toString() + "\n");
		
        stringbuffer.append("SELECT ");
        if(select_ != null && select_.size() != 0)
        	stringbuffer.append(select_.elementAt(0).toString());
        for(int i = 1; i < select_.size(); i++)
            stringbuffer.append(", " + select_.elementAt(i).toString());


        if(from_ != null)
            stringbuffer.append(" FROM " + from_.toString());

        if(where_ != null)
            stringbuffer.append(" WHERE " + where_.toString());
        if(sample_ != null)
        	stringbuffer.append(" "  + sample_.toString());
        if(sample_ != null)
        	stringbuffer.append(" "  + sample_.toString());
        if(case_ != null)
        	stringbuffer.append(" " + case_.toString());
        
        return stringbuffer.toString();
    }
	
	/**
	 * @brief Marshalls the Event into a string and encodes it using Base64 coding
	 * @return A string that is Base64 encoded containing the marshalled event
	 * @throws IOException
	 */
	public String marshall() throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ObjectOutputStream out = new ObjectOutputStream(baos);
		out.writeObject(this);
		out.close();
		byte[] marshalledEvent = baos.toByteArray();
		String base64EndocdedMarshalledEvent = Base64Coder.encodeLines(marshalledEvent);
		return(base64EndocdedMarshalledEvent);
	}
	
	/**
	 * @brief Decodes the string from Base64 coding and unmarshalls the event and returns it
	 * @param base64EncodedMarshalledEvent A String that is encoded using Base64 coding and contains
	 *        a marshalled event
	 * @return The event object that was marshalled and encoded in the argument string 
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public static SNQLQuery unmarshall(String base64EncodedMarshalledEvent)
	throws IOException, ClassNotFoundException {
		byte[] marshalledEvent = Base64Coder.decodeLines(base64EncodedMarshalledEvent);
		ObjectInputStream ois =
			new ObjectInputStream(new ByteArrayInputStream(marshalledEvent));
		SNQLQuery o = (SNQLQuery)ois.readObject();
		ois.close();
		return(o);
	}
}
